// RPC stubs for clients to talk to lock_server, and cache the locks
// see lock_client.cache.h for protocol details.

#include "lock_client_cache.h"
#include "rpc.h"
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <map>

#define REVOKED 1
#define NOT_REVOKED 0
#define CAN_RETRY 1
#define DO_NOT_RETRY 0


static void *
releasethread(void *x)
{
  lock_client_cache *cc = (lock_client_cache *) x;
  cc->releaser();
  return 0;
}



int lock_client_cache::last_port = 0;

lock_client_cache::lock_client_cache(std::string xdst, 
				     class lock_release_user *_lu)
  : lock_client(xdst), lu(_lu)
{
  srand(time(NULL)^last_port);
  rlock_port = ((rand()%32000) | (0x1 << 10));
  const char *hname;
  // assert(gethostname(hname, 100) == 0);
  hname = "127.0.0.1";
  std::ostringstream host;
  host << hname << ":" << rlock_port;
 
  //our id
  id = host.str();
  last_port = rlock_port;

  rpcs *rlsrpc = new rpcs(htons(rlock_port));
  lock_client_cache lc;
  
  
  /* register RPC handlers with rlsrpc */
  (*rlsrpc).reg(rlock_protocol::revoke, &lc, &lock_client_cache::revoke);
  (*rlsrpc).reg(rlock_protocol::retry, &lc, &lock_client_cache::retry);
  
  pthread_mutex_init(&lock_cache_mutex, NULL);
  pthread_cond_init(&lock_cache_cond_mutex, NULL);  
  pthread_t th;
  int r = pthread_create(&th, NULL, &releasethread, (void *) this);
  assert (r == 0);
}


void
lock_client_cache::releaser()
{

  // This method should be a continuous loop, waiting to be notified of
  // freed locks that have been revoked by the server, so that it can
  // send a release RPC.
  
  /*  pthread_mutex_unlock(&lock_cache_mutex);
  while (1) {
    map<lock_protocol::lockid_t, lock_protocol::lock_status>::iterator it;
    map<lock_protocol::Lockid_t, lock_protocol::lock_status>::iterator check;
    

    pthread_cond_wait(&lock_cache_cond_mutex, &lock_cache_mutex);
    for (it = lock_cache.begin(); it != lock_cache.end(); it++){
      if ((it->second) == REVOKED){
	//SEND MSG BACK TO USER

	int r;
	int ret = cl->call(lock_protocol::release, lid, r);
	it->second = NONE;
	
	pthread_cond_broadcast(&lock_cache_cond_mutex);
	//debug
	check =lock_cache.find(it->first);
	if (check-> second != NONE){
	  printf("in lock_client releaseer, not resetting values properly\n");
	}
      }
    }
    
  }
  pthread_mutex_unlock(&lock_cache_mutex);
  */
}


lock_protocol::status
lock_client_cache::acquire(lock_protocol::lockid_t lid){
  printf("RECEIVED ACQUIRE IN CLIENT \n");
  int k;
  c1->call(lock_protocol::acquire, lid, cl.id, k);
  printf("send acquire request for lock: %llu with my id: %s\n", lid, (cl.id).c_str());
  return lock_protocol::OK;

  /*  pthread_mutex_lock(&lock_cache_mutex);
  map<lock_protocol::lockid_t, lock_protocol::lock_status>::iterator it;
  map<lock_protocol::lockid_t, lock_protocol::lock_status>::iterator check;
  it = lock_cache.find(lid);

  int made_req  = 0;
  
  while (1) {
    if ((it == (lock_cache.end())) || (it->second == NONE)){
      if (it->second == NONE ) {
	lock_cache.erase(it);
      }
      
      int ret;
      if (made_req == 0){
	made_req = 0;
	ret = cl->call(lock_protocol::acquire, cl.id, lid, r);
      }else {
	ret = retry;
      }
      
      if (ret == OK){
	//check whether its been revoked
	//if not, insert into map and take it
	
      }
      
      if (ret == retry){
	
      }
    }
      
      // GET LOCK FROM CLIENT
      
	break;
    }
    
  } else if ((it->second == LOCKED) || (it->second == REVOKED)) {
    pthread_cond_wait(&cache_lock_cond_mutex, &cache_lock_mutex);
    
  } else {//FREE
    //take it
    /change in map

  }
  }*/
}

lock_protocol::status
lock_client_cache::release(lock_protocol::lockid_t lid, int &r){
  return lock_protocol::OK;
  /*  pthread_mutex_lock(&lock_cache_mutex);
  map<lock_protocol::lockid_t, lock_protocol::lock_status>::iterator it;
  map<lock_protocol::lockid_t, lock_protocol::lock_status>::iterator check;
  
  it = lock_cache.find(lid);
  
  if (it != (lock_cache.end())){
    it->second = FREE;
    pthread_cond_broadcast(&cache_lock_cond_mutex);
    if (check-> second != FREE){
      printf("in lock_client release, not resetting values properly\n");
    }
  pthread_mutex_unlock(&lock_cache_mutex);
  return OK;
  } else {
    pthread_mutex_unlock(&lock_cache_mutex);
    return IOERR;
  } 
  */
}

int lock_client_cache::revoke(lock_protocol::lockid_t lid, int random, int &r){
  //add to list
  //let releaser know
  printf("Received revoke from server for %llu\n", lid);
  return lock_protocol::OK;
  /*  pthread_mutex_lock(&lock_cache_mutex);
  map<lock_protocol::lockid_t, lock_protocol::lock_status>::iterator it;
  map<lock_protocol::lockid_t, lock_protocol::lock_status>::iterator check;
  it = lock_cache.find(lid);
  if ((it != (lock_cache.end())) && (it->second == FREE)){
    it->second = REVOKED;
    pthread_cond_broadcast(&lock_cache_cond_mutex);
    //debug
    check =lock_cache.find(it->first);
    if (check-> second != REVOKED){
      printf("in lock_client revoke, not resetting values properly\n");
    }
  }
  pthread_mutex_unlock(&lock_cache_mutex);
  return OK;
  */
}


int lock_client_cache::retry(lock_protocol::lockid_t lid, int random, int &r){
  return lock_protocol::OK;
}
